﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using AutoLoL.Entities;
using AutoLoL.Logic.Parsers;
using AutoLoL.Logic.Utils;
using AutoLoL.Shared;
using Ionic.Zip;

namespace AutoLoL.Logic.Game
{
    [Obsolete]
    public class PakDataProvider
    {
        public void GetChampionData(BackgroundWorker backgroundWorker)
        {
            string pakPath = Path.Combine(UserSettings.Instance.LolDirectory, "Game", "HeroPak_client.zip");
            string fontconfigPath = Path.Combine(UserSettings.Instance.LolDirectory, "Game", "Data", "Menu", "fontconfig_en_GB.txt");

            if (!File.Exists(pakPath) || !File.Exists(fontconfigPath))
            {
                return;
            }

            if (!Directory.Exists(Constants.TEMP_DIRECTORY))
            {
                Directory.CreateDirectory(Constants.TEMP_DIRECTORY);
            }

            using (ZipFile zipFile = new ZipFile(pakPath))
            {
                XDocument championsDoc = new XDocument();
                XElement championsElement = new XElement("champions");

                // Get all character inibin files
                ZipEntry[] characterEntries = zipFile.Entries.Where(c =>
                    // data\characters directory
                    c.FileName.ReplaceForwardWithBackwardSlash().ToLower().StartsWith("data\\characters\\")
                        // only inibin files
                    && c.FileName.ToLower().EndsWith(".inibin")
                        // 3 directories and 1 file
                    && c.FileName.ReplaceForwardWithBackwardSlash().Split('\\').Length == 4).ToArray();

                int processed = 0;
                foreach (ZipEntry zipEntry in characterEntries)
                {
                    string absoluteInibinPath = ExtractZipEntry(zipEntry, Constants.TEMP_DIRECTORY);
                    Dictionary<uint, InibinEntry> inibinData = InibinParser.ParseFile(absoluteInibinPath);
                    TryDeleteFile(absoluteInibinPath);

                    // Only parse champions
                    if (inibinData.ContainsKey((uint)InibinKeys.Character.IS_CHAMPION) && inibinData[(uint)InibinKeys.Character.IS_CHAMPION].GetString().ToLower().Equals("true"))
                    {
                        string[] inibinZipPathSections = zipEntry.FileName.ReplaceForwardWithBackwardSlash().Split('\\');

                        // Champ directory
                        string championDirectoryName = inibinZipPathSections.Skip(inibinZipPathSections.Length - 2).Take(1).Single();
                        string absoluteChampionDirectory = Path.Combine(Constants.CHAMPIONS_DIRECTORY, championDirectoryName);

                        // Parse FC
                        string fckName = inibinData[(uint)InibinKeys.Character.FCK_NAME].GetString();
                        string fckDescription = inibinData[(uint)InibinKeys.Character.FCK_DESCRIPTION].GetString();
                        string fckLore = inibinData[(uint)InibinKeys.Character.FCK_LORE].GetString();

                        string name = FontConfigParser.Instance.GetValue(fckName);
                        string description = FontConfigParser.Instance.GetValue(fckDescription);
                        string lore = FontConfigParser.Instance.GetValue(fckLore);

                        // XML
                        XElement championElement = new XElement("champion");
                        championElement.Add(new XElement("name", name));
                        championElement.Add(new XElement("description", description));
                        championElement.Add(new XElement("lore", lore));
                        championElement.Add(new XElement("directory", championDirectoryName));

                        // Create directory for models and images
                        if (!Directory.Exists(absoluteChampionDirectory))
                        {
                            Directory.CreateDirectory(absoluteChampionDirectory);
                        }

                        #region Icon
                        string iconDdsFileName = inibinData[(uint)InibinKeys.Character.PATH_ICON_DDS].GetString();
                        List<string> iconDdsZipPathSections = inibinZipPathSections.Take(inibinZipPathSections.Length - 1).ToList();
                        iconDdsZipPathSections.Add("Info");
                        iconDdsZipPathSections.Add(iconDdsFileName);
                        //string iconJpegFileName = ConvertFile(zipFile, iconDdsZipPathSections, absoluteChampionDirectory, new DdsFileConverter());
                        //championElement.Add(new XElement("icon", iconJpegFileName));
                        #endregion

                        #region Skins
                        XElement skinsElement = new XElement("skins");
                        for (int i = 0; i < InibinKeys.CHARACTER_MODEL_DDS_PATH_KEYS.Length; i++)
                        {
                            if (inibinData.ContainsKey(InibinKeys.CHARACTER_MODEL_DDS_PATH_KEYS[i]))
                            {
                                XElement skinElement = new XElement("skin");

                                string modelDdsFileName = inibinData[InibinKeys.CHARACTER_MODEL_DDS_PATH_KEYS[i]].GetString();
                                List<string> modelDdsZipPathSections = inibinZipPathSections.Take(inibinZipPathSections.Length - 1).ToList();
                                modelDdsZipPathSections.Add(modelDdsFileName);
                                //string modelJpegFileName = ConvertFile(zipFile, modelDdsZipPathSections, absoluteChampionDirectory, new DdsFileConverter());
                                //skinElement.Add(new XAttribute("jpeg", modelJpegFileName));

                                string modelSknFileName = championDirectoryName + ".skn";
                                if (inibinData.ContainsKey(InibinKeys.CHARACTER_MODEL_SKN_PATH_KEYS[i]))
                                {
                                    modelSknFileName = inibinData[InibinKeys.CHARACTER_MODEL_SKN_PATH_KEYS[i]].GetString();
                                }

                                List<string> modelSknZipPathSections = inibinZipPathSections.Take(inibinZipPathSections.Length - 1).ToList();
                                modelSknZipPathSections.Add(modelSknFileName);
                                //string modelObjFileName = ConvertFile(zipFile, modelSknZipPathSections, absoluteChampionDirectory, new SknFileConverter());
                                //skinElement.Add(new XAttribute("obj", modelObjFileName));

                                string skinName = "Base";
                                if (i > 0)
                                {
                                    string fckSkin = string.Format("game_character_skin_displayname_{0}_{1}", championDirectoryName, i);
                                    if (FontConfigParser.Instance.ContainsKey(fckSkin))
                                    {
                                        skinName = FontConfigParser.Instance.GetValue(fckSkin);
                                    }
                                    else
                                    {
                                        skinName = "Unknown";
                                    }
                                }

                                skinElement.Add(new XAttribute("name", skinName));
                                skinsElement.Add(skinElement);
                            }
                        }

                        championElement.Add(skinsElement);
                        #endregion

                        championsElement.Add(championElement);
                    }

                    processed++;

                    int progress = (int)Math.Round(((double)processed / (double)characterEntries.Length) * 100.0);
                    backgroundWorker.ReportProgress(progress);
                }

                championsDoc.Add(championsElement);
                championsDoc.Save(Constants.CHAMPIONS_XML_FILE);
            }
        }

        public void GetItemData(BackgroundWorker backgroundWorker)
        {
            string pakPath = Path.Combine(UserSettings.Instance.LolDirectory, "Game", "HeroPak_client.zip");

            if (!File.Exists(pakPath))
            {
                return;
            }

            if (!Directory.Exists(Constants.TEMP_DIRECTORY))
            {
                Directory.CreateDirectory(Constants.TEMP_DIRECTORY);
            }

            if (!Directory.Exists(Constants.ITEMS_DIRECTORY))
            {
                Directory.CreateDirectory(Constants.ITEMS_DIRECTORY);
            }

            using (ZipFile zipFile = new ZipFile(pakPath))
            {
                XDocument itemsDoc = new XDocument();
                XElement itemsElement = new XElement("items");

                // Get all item inibin files
                ZipEntry[] itemEntries = zipFile.Entries.Where(c =>
                    c.FileName.ToLower().ReplaceForwardWithBackwardSlash().StartsWith("data\\items\\")
                    && c.FileName.ReplaceForwardWithBackwardSlash().ToLower().EndsWith(".inibin")).ToArray();

                int processed = 0;
                foreach (ZipEntry zipEntry in itemEntries)
                {
                    string absoluteInibinPath = ExtractZipEntry(zipEntry, Constants.TEMP_DIRECTORY);
                    Dictionary<uint, InibinEntry> inibinData = InibinParser.ParseFile(absoluteInibinPath);
                    TryDeleteFile(absoluteInibinPath);

                    // Only parse valid items
                    if (inibinData.ContainsKey((uint)InibinKeys.Item.IS_VALID) && inibinData[(uint)InibinKeys.Item.IS_VALID].GetBit())
                    {
                        string[] inibinZipPathSections = zipEntry.FileName.ReplaceForwardWithBackwardSlash().Split('\\');

                        // Parse FC
                        string fckName = inibinData[(uint)InibinKeys.Item.FCK_NAME].GetString();
                        string fckDescription = inibinData[(uint)InibinKeys.Item.FCK_DESCRIPTION].GetString();

                        string id = fckDescription.Substring(22);
                        string name = FontConfigParser.Instance.GetValue(fckName);
                        string description = FontConfigParser.Instance.GetValue(fckDescription);
                        string price = inibinData[(uint)InibinKeys.Item.INT_PRICE].ToString();

                        // XML
                        XElement itemElement = new XElement("item");
                        itemElement.Add(new XElement("id", id));
                        itemElement.Add(new XElement("name", name));
                        itemElement.Add(new XElement("description", description));
                        itemElement.Add(new XElement("price", price));

                        #region Icon
                        string iconDdsFileName = inibinData[(uint)InibinKeys.Item.PATH_ICON_DDS].GetString();
                        List<string> iconDdsZipPathSections = inibinZipPathSections.Take(inibinZipPathSections.Length - 1).ToList();
                        iconDdsZipPathSections.Add("Icons2D");
                        iconDdsZipPathSections.Add(iconDdsFileName);
                        //string iconJpegFileName = ConvertFile(zipFile, iconDdsZipPathSections, Constants.ITEMS_DIRECTORY, new DdsFileConverter());
                        //itemElement.Add(new XElement("icon", iconJpegFileName));
                        #endregion

                        #region BuildsInto
                        XElement buildsIntoElements = new XElement("builds_into");
                        for (int i = 0; i < InibinKeys.ITEM_BUILDS_INTO_KEYS.Length; i++)
                        {
                            if (inibinData.ContainsKey(InibinKeys.ITEM_BUILDS_INTO_KEYS[i]))
                            {
                                string buildsIntoIdString = inibinData[InibinKeys.ITEM_BUILDS_INTO_KEYS[i]].ToString();
                                int buildsIntoIdInt = int.Parse(buildsIntoIdString);
                                if (buildsIntoIdInt > 0)
                                {
                                    XElement buildsIntoElement = new XElement("item");
                                    buildsIntoElement.Add(new XAttribute("id", buildsIntoIdInt));
                                    buildsIntoElements.Add(buildsIntoElement);
                                }
                                else
                                {
                                    //Console.WriteLine(string.Format("Builds into '{0}' for item '{1}'", buildsIntoIdInt, name));
                                }
                            }
                        }

                        itemElement.Add(buildsIntoElements);
                        #endregion

                        #region BuiltFrom
                        XElement builtFromElements = new XElement("built_from");
                        for (int i = 0; i < InibinKeys.ITEM_BUILT_FROM_KEYS.Length; i++)
                        {
                            if (inibinData.ContainsKey(InibinKeys.ITEM_BUILT_FROM_KEYS[i]))
                            {
                                string builtFromIdString = inibinData[InibinKeys.ITEM_BUILT_FROM_KEYS[i]].ToString();
                                int builtFromIdInt = int.Parse(builtFromIdString);
                                if (builtFromIdInt != 0)
                                {
                                    XElement builtFromElement = new XElement("item");
                                    builtFromElement.Add(new XAttribute("id", builtFromIdInt));
                                    builtFromElements.Add(builtFromElement);
                                }
                                else
                                {
                                    //Console.WriteLine(string.Format("Built from '{0}' for item '{1}'", builtFromIdInt, name));
                                }
                            }
                        }

                        itemElement.Add(builtFromElements);
                        #endregion

                        itemsElement.Add(itemElement);
                    }

                    processed++;
                    int progress = (int)Math.Round(((double)processed / (double)itemEntries.Length) * 100.0);
                    backgroundWorker.ReportProgress(progress);
                }

                itemsDoc.Add(itemsElement);
                itemsDoc.Save(Constants.ITEMS_XML_FILE);
            }
        }

        public void GetMasteryDataData(BackgroundWorker backgroundWorker)
        {
            string pakPath = Path.Combine(UserSettings.Instance.LolDirectory, "Game", "HeroPak_client.zip");
            string iconsDirectory = Path.Combine(UserSettings.Instance.LolDirectory, "Air", "Assets", "Images", "MasteryIcons");

            if (!File.Exists(pakPath))
            {
                return;
            }

            if (!Directory.Exists(Constants.TEMP_DIRECTORY))
            {
                Directory.CreateDirectory(Constants.TEMP_DIRECTORY);
            }

            if (!Directory.Exists(Constants.MASTERIES_DIRECTORY))
            {
                Directory.CreateDirectory(Constants.MASTERIES_DIRECTORY);
            }

            using (ZipFile zipFile = new ZipFile(pakPath))
            {
                ZipEntry talentDocEntry = zipFile.Entries.Where(c => c.FileName.ReplaceForwardWithBackwardSlash().ToLower().Equals("data\\talents\\talenttree.xml")).Single();
                string absoluteTalentDocPath = ExtractZipEntry(talentDocEntry, Constants.TEMP_DIRECTORY);
                XDocument talentDoc = XDocument.Load(absoluteTalentDocPath);
                TryDeleteFile(absoluteTalentDocPath);

                XDocument masteryDoc = new XDocument();
                XElement masteriesElement = new XElement("masteries");
                foreach (XElement talentGroupElement in talentDoc.Root.Elements("TalentGroup"))
                {
                    string treeName = talentGroupElement.Attribute("name").Value;

                    XElement masteryTreeElement = new XElement("tree");
                    masteryTreeElement.Add(new XAttribute("name", treeName));

                    foreach (XElement talentRowElement in talentGroupElement.Elements("TalentRow"))
                    {
                        int pointsRequired = int.Parse(talentRowElement.Attribute("pointsToActivate").Value);

                        XElement masteryRowElement = new XElement("row");
                        masteryRowElement.Add(new XAttribute("required", pointsRequired));

                        foreach (XElement talentElement in talentRowElement.Elements("Talent"))
                        {
                            int masteryId = int.Parse(talentElement.Attribute("gameCode").Value);
                            int column = int.Parse(talentElement.Attribute("column").Value);
                            int maximumValue = int.Parse(talentElement.Element("MaxRank").Value);

                            XElement masteryElement = new XElement("mastery");
                            masteryElement.Add(new XElement("id", masteryId));
                            masteryElement.Add(new XElement("max", maximumValue));
                            masteryElement.Add(new XElement("column", column));

                            if (talentElement.Element("PrereqTalentGameCode") != null)
                            {
                                masteryElement.Add(new XElement("prereq", int.Parse(talentElement.Element("PrereqTalentGameCode").Value)));
                            }

                            ZipEntry talentInbinEntry = zipFile.Entries.Where(c => c.FileName.ReplaceForwardWithBackwardSlash().ToLower().Equals(string.Format("data\\talents\\{0}.inibin", masteryId))).Single();
                            string absoluteInibinPath = ExtractZipEntry(talentInbinEntry, Constants.TEMP_DIRECTORY);
                            Dictionary<uint, InibinEntry> inibinData = InibinParser.ParseFile(absoluteInibinPath);
                            TryDeleteFile(absoluteInibinPath);

                            string fckName = inibinData[(uint)InibinKeys.Mastery.FCK_NAME].GetString();

                            string name = FontConfigParser.Instance.GetValue(fckName);
                            masteryElement.Add(new XElement("name", name));

                            #region Icon
                            string iconFileName = string.Format("{0}.png", masteryId);
                            string sourceIconPath = Path.Combine(iconsDirectory, iconFileName);
                            string targetIconPath = Path.Combine(Constants.MASTERIES_DIRECTORY, iconFileName);
                            
                            File.Copy(sourceIconPath, targetIconPath, true);
                            masteryElement.Add(new XElement("icon", iconFileName));
                            #endregion

                            #region Descriptions
                            XElement descriptionsElement = new XElement("descriptions");
                            for (int i = 0; i < InibinKeys.MASTERY_LEVEL_DESCRIPTION_KEYS.Length; i++)
                            {
                                string fckDescription = inibinData[InibinKeys.MASTERY_LEVEL_DESCRIPTION_KEYS[i]].GetString();
                                string description = FontConfigParser.Instance.GetValue(fckDescription);

                                if (!string.IsNullOrEmpty(description))
                                {
                                    XElement descriptionElement = new XElement("description");
                                    descriptionElement.Add(new XAttribute("value", (i + 1)));
                                    descriptionElement.Add(new XAttribute("description", description));
                                    descriptionsElement.Add(descriptionElement);
                                }
                            }
                            masteryElement.Add(descriptionsElement);
                            #endregion

                            masteryRowElement.Add(masteryElement);
                        }

                        masteryTreeElement.Add(masteryRowElement);
                    }

                    masteriesElement.Add(masteryTreeElement);
                }

                masteryDoc.Add(masteriesElement);
                masteryDoc.Save(Constants.MASTERIES_XML_FILE);
            }

            backgroundWorker.ReportProgress(100);
        }

        public void GetSummonerSpellData(BackgroundWorker backgroundWorker)
        {
            string pakPath = Path.Combine(UserSettings.Instance.LolDirectory, "Game", "HeroPak_client.zip");

            if (!File.Exists(pakPath))
            {
                return;
            }

            if (!Directory.Exists(Constants.TEMP_DIRECTORY))
            {
                Directory.CreateDirectory(Constants.TEMP_DIRECTORY);
            }

            if (!Directory.Exists(Constants.SPELLS_DIRECTORY))
            {
                Directory.CreateDirectory(Constants.SPELLS_DIRECTORY);
            }

            using (ZipFile zipFile = new ZipFile(pakPath))
            {
                XDocument spellsDoc = new XDocument();
                XElement spellsElement = new XElement("spells");

                // Get all summoner spell inibin files
                ZipEntry[] summonerSpellEntries = zipFile.Entries.Where(c =>
                    // data\spells directory
                    c.FileName.ReplaceForwardWithBackwardSlash().ToLower().StartsWith("data\\spells\\summoner")
                        // only inibin files
                    && c.FileName.ToLower().EndsWith(".inibin")
                        // 2 directories and 1 file
                    && c.FileName.ReplaceForwardWithBackwardSlash().Split('\\').Length == 3).ToArray();

                foreach (ZipEntry zipEntry in summonerSpellEntries)
                {
                    string[] inibinZipPathSections = zipEntry.FileName.ReplaceForwardWithBackwardSlash().Split('\\');

                    string absoluteInibinPath = ExtractZipEntry(zipEntry, Constants.TEMP_DIRECTORY);
                    Dictionary<uint, InibinEntry> inibinData = InibinParser.ParseFile(absoluteInibinPath);
                    TryDeleteFile(absoluteInibinPath);

                    if (inibinData.ContainsKey((uint)InibinKeys.SummonerSpell.IS_VALID) && inibinData[(uint)InibinKeys.SummonerSpell.IS_VALID].GetString().ToLower().Equals("true"))
                    {
                        // Parse FC
                        string fckName = inibinData[(uint)InibinKeys.SummonerSpell.FCK_NAME].GetString();
                        string fckDescription = inibinData[(uint)InibinKeys.SummonerSpell.FCK_DESCRIPTION].GetString();

                        string name = FontConfigParser.Instance.GetValue(fckName);
                        string description = FontConfigParser.Instance.GetValue(fckDescription);

                        // XML
                        XElement summonerSpellElement = new XElement("spell");
                        summonerSpellElement.Add(new XElement("name", name));
                        summonerSpellElement.Add(new XElement("description", description));

                        #region Icon
                        string iconDdsFileName = inibinData[(uint)InibinKeys.SummonerSpell.PATH_DDS_ICON].GetString();
                        List<string> iconDdsZipPathSections = inibinZipPathSections.Take(inibinZipPathSections.Length - 1).ToList();
                        iconDdsZipPathSections.Add("Icons2D");
                        iconDdsZipPathSections.Add(iconDdsFileName);
                        //string iconJpegFileName = ConvertFile(zipFile, iconDdsZipPathSections, Constants.SPELLS_DIRECTORY, new DdsFileConverter());
                        //summonerSpellElement.Add(new XElement("icon", iconJpegFileName));
                        #endregion

                        spellsElement.Add(summonerSpellElement);
                    }
                }

                spellsDoc.Add(spellsElement);
                spellsDoc.Save(Constants.SPELLS_XML_FILE);
            }

            backgroundWorker.ReportProgress(100);
        }

        private string ExtractZipEntry(ZipEntry entry, string directory)
        {
            entry.Extract(directory, ExtractExistingFileAction.OverwriteSilently);
            string[] pathSections = entry.FileName.ReplaceForwardWithBackwardSlash().Split('\\');
            string relativePath = Path.Combine(pathSections);
            string absolutePath = Path.Combine(directory, relativePath);
            return absolutePath;
        }

        //private string ConvertFile(ZipFile zipFile, List<string> zipPathSections, string targetDirectory, IFileConverter fileConverter)
        //{
        //    string tempZipPath = string.Join("\\", zipPathSections);
        //    string relativeTempPath = Path.Combine(zipPathSections.ToArray());
        //    string absoluteTempPath = Path.Combine(Constants.TEMP_DIRECTORY, relativeTempPath);

        //    ZipEntry tempZipEntry = zipFile.Entries.Where(c => c.FileName.ReplaceForwardWithBackwardSlash().ToLower().Equals(tempZipPath.ReplaceForwardWithBackwardSlash().ToLower())).Single();
        //    ExtractZipEntry(tempZipEntry, Constants.TEMP_DIRECTORY);

        //    string fileName = fileConverter.ConvertFile(absoluteTempPath, targetDirectory, true);
        //    TryDeleteFile(absoluteTempPath);

        //    return fileName;
        //}

        private void TryDeleteFile(string filePath)
        {
            try { File.Delete(filePath); }
            catch { if (Debugger.IsAttached) throw; }
        }
    }
}
